Dansk

Udforsk TypeScript diskriminerende unioner, et kraftfuldt værktøj til at bygge robuste og typesikre tilstandsstyringsmekanismer. Lær at definere.

TypeScript Diskiminerende Unioner: Bygning af Typesikre Tilstandsstyringsmekanismer

Inden for softwareudvikling er effektiv styring af applikationstilstand afgørende. Tilstandsstyringsmekanismer giver en kraftfuld abstraktion til modellering af komplekse tilstandsfulde systemer, der sikrer forudsigelig adfærd og forenkler ræsonnementet om systemets logik. TypeScript tilbyder med sit robuste typesystem en fantastisk mekanisme til at bygge typesikre tilstandsstyringsmekanismer ved hjælp af diskriminerende unioner (også kendt som taggede unioner eller algebraiske datatyper).

Hvad er Diskiminerende Unioner?

En diskriminerende union er en type, der repræsenterer en værdi, som kan være en af flere forskellige typer. Hver af disse typer, kendt som medlemmer af unionen, deler en fælles, distinkt egenskab kaldet diskriminanten eller tagget. Denne diskriminant gør det muligt for TypeScript præcist at bestemme, hvilket medlem af unionen der er aktivt, hvilket muliggør kraftfuld typekontrol og autofuldførelse.

Tænk på det som et trafiklys. Det kan være i en af tre tilstande: Rød, Gul eller Grøn. Egenskaben 'farve' fungerer som diskriminanten og fortæller os præcist, hvilken tilstand lyset er i.

Hvorfor Bruge Diskiminerende Unioner til Tilstandsstyringsmekanismer?

Diskiminerende unioner medfører flere nøglefordele ved opbygning af tilstandsstyringsmekanismer i TypeScript:

Definering af en Tilstandsstyringsmekanisme med Diskiminerende Unioner

Lad os illustrere, hvordan man definerer en tilstandsstyringsmekanisme ved hjælp af diskriminerende unioner med et praktisk eksempel: et ordrestyringssystem. En ordre kan være i følgende tilstande: Afventer, Behandles, Afsendt og Leveret.

Trin 1: Definer Tilstandstyperne

Først definerer vi de individuelle typer for hver tilstand. Hver type vil have en `type`-egenskab, der fungerer som diskriminanten, sammen med enhver tilstandsspecifik data.


interface Pending {
  type: "pending";
  orderId: string;
  customerName: string;
  items: string[];
}

interface Processing {
  type: "processing";
  orderId: string;
  assignedAgent: string;
}

interface Shipped {
  type: "shipped";
  orderId: string;
  trackingNumber: string;
}

interface Delivered {
  type: "delivered";
  orderId: string;
  deliveryDate: Date;
}

Trin 2: Opret den Diskiminerende Unionstype

Derefter opretter vi den diskriminerende union ved at kombinere disse individuelle typer ved hjælp af `|` (union)-operatoren.


type OrderState = Pending | Processing | Shipped | Delivered;

Nu repræsenterer `OrderState` en værdi, der kan være enten `Pending`, `Processing`, `Shipped` eller `Delivered`. `type`-egenskaben inden i hver tilstand fungerer som diskriminanten, hvilket gør det muligt for TypeScript at differentiere mellem dem.

Håndtering af Tilstandsovergange

Nu hvor vi har defineret vores tilstandsstyringsmekanisme, har vi brug for en mekanisme til at overgå mellem tilstandene. Lad os oprette en `processOrder`-funktion, der tager den aktuelle tilstand og en handling som input og returnerer den nye tilstand.


interface Action {
  type: string;
  payload?: any;
}

function processOrder(state: OrderState, action: Action): OrderState {
  switch (state.type) {
    case "pending":
      if (action.type === "startProcessing") {
        return {
          type: "processing",
          orderId: state.orderId,
          assignedAgent: action.payload.agentId,
        };
      }
      return state; // Ingen tilstandsændring

    case "processing":
      if (action.type === "shipOrder") {
        return {
          type: "shipped",
          orderId: state.orderId,
          trackingNumber: action.payload.trackingNumber,
        };
      }
      return state; // Ingen tilstandsændring

    case "shipped":
      if (action.type === "deliverOrder") {
        return {
          type: "delivered",
          orderId: state.orderId,
          deliveryDate: new Date(),
        };
      }
      return state; // Ingen tilstandsændring

    case "delivered":
      // Ordre er allerede leveret, ingen yderligere handlinger
      return state;

    default:
      // Dette bør aldrig ske på grund af udtømmende kontrol
      return state; // Eller kast en fejl
  }
}

Forklaring

Udnyt Udtømmende Kontrol

TypeScripts udtømmende kontrol er en kraftfuld funktion, der sikrer, at du håndterer alle mulige tilstande i din tilstandsstyringsmekanisme. Hvis du tilføjer en ny tilstand til `OrderState`-unionen, men glemmer at opdatere `processOrder`-funktionen, vil TypeScript rapportere en fejl.

For at aktivere udtømmende kontrol kan du bruge `never`-typen. Inden i `default`-caset i din switch-erklæring tildeler du tilstanden til en variabel af typen `never`.


function processOrder(state: OrderState, action: Action): OrderState {
  switch (state.type) {
    // ... (foregående cases) ...

    default:
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck; // Eller kast en fejl
  }
}

Hvis `switch`-erklæringen håndterer alle mulige `OrderState`-værdier, vil `_exhaustiveCheck`-variablen have typen `never`, og koden kompileres. Men hvis du tilføjer en ny tilstand til `OrderState`-unionen og glemmer at håndtere den i `switch`-erklæringen, vil `_exhaustiveCheck`-variablen have en anden type, og TypeScript vil udstede en kompileringstidsfejl, der advarer dig om den manglende case.

Praktiske Eksempler og Anvendelser

Diskiminerende unioner er anvendelige i en bred vifte af scenarier ud over simpel ordrestyring:

Eksempel: UI Tilstandsstyring

Lad os overveje et simpelt eksempel på styring af tilstanden af en UI-komponent, der henter data fra et API. Vi kan definere følgende tilstande:


interface Initial {
  type: "initial";
}

interface Loading {
  type: "loading";
}

interface Success {
  type: "success";
  data: T;
}

interface Error {
  type: "error";
  message: string;
}

type UIState = Initial | Loading | Success | Error;

function renderUI(state: UIState): React.ReactNode {
  switch (state.type) {
    case "initial":
      return 

Klik på knappen for at indlæse data.

; case "loading": return

Indlæser...

; case "success": return
{JSON.stringify(state.data, null, 2)}
; case "error": return

Fejl: {state.message}

; default: const _exhaustiveCheck: never = state; return _exhaustiveCheck; } }

Dette eksempel viser, hvordan diskriminerende unioner kan bruges til effektivt at styre de forskellige tilstande i en UI-komponent, hvilket sikrer, at UI'en renderes korrekt baseret på den aktuelle tilstand. `renderUI`-funktionen håndterer hver tilstand passende og giver en klar og typesikker måde at styre UI'en på.

Bedste Praksisser for Brug af Diskiminerende Unioner

For effektivt at udnytte diskriminerende unioner i dine TypeScript-projekter, overvej følgende bedste praksisser:

Avancerede Teknikker

Betingede Typer

Betingede typer kan kombineres med diskriminerende unioner for at skabe endnu mere kraftfulde og fleksible tilstandsstyringsmekanismer. Du kan for eksempel bruge betingede typer til at definere forskellige returtyper for en funktion baseret på den aktuelle tilstand.


function getData(state: UIState): T | undefined {
  if (state.type === "success") {
    return state.data;
  }
  return undefined;
}

Denne funktion bruger en simpel `if`-erklæring, men kunne gøres mere robust ved hjælp af betingede typer for at sikre, at en bestemt type altid returneres.

Utility Typer

TypeScripts utility typer, såsom `Extract` og `Omit`, kan være nyttige, når man arbejder med diskriminerende unioner. `Extract` giver dig mulighed for at udtrække specifikke medlemmer fra en unionstype baseret på en betingelse, mens `Omit` giver dig mulighed for at fjerne egenskaber fra en type.


// Ekstraher "success" tilstanden fra UIState unionen
type SuccessState = Extract, { type: "success" }>;

// Fjern "message" egenskaben fra Error interfacet
type ErrorWithoutMessage = Omit;

Virksomhedseksempler på Tværs af Forskellige Brancher

Kraften i diskriminerende unioner strækker sig over forskellige brancher og applikationsdomæner:

Konklusion

TypeScripts diskriminerende unioner tilbyder en kraftfuld og typesikker måde at bygge tilstandsstyringsmekanismer på. Ved tydeligt at definere de mulige tilstande og overgange kan du skabe mere robust, vedligeholdelsesvenlig og forståelig kode. Kombinationen af typesikkerhed, udtømmende kontrol og forbedret kodes fuldførelse gør diskriminerende unioner til et uvurderligt værktøj for enhver TypeScript-udvikler, der beskæftiger sig med kompleks tilstandsstyring. Omfavn diskriminerende unioner i dit næste projekt og oplev fordelene ved typesikker tilstandsstyring førstehånds. Som vi har vist med forskellige eksempler fra e-handel til sundhedspleje og logistik til uddannelse, er princippet om typesikker tilstandsstyring gennem diskriminerende unioner universelt anvendeligt.

Uanset om du bygger en simpel UI-komponent eller en kompleks virksomhedsapplikation, kan diskriminerende unioner hjælpe dig med at styre tilstand mere effektivt og reducere risikoen for runtime-fejl. Så dyk ned og udforsk verden af typesikre tilstandsstyringsmekanismer med TypeScript!